#include "poller.hpp"
#include "event_trace.hpp"
#include <error.h>

using namespace event_framework;

bool Poller::Init()
{
    epfd = epoll_create1(EPOLL_CLOEXEC);
    if (epfd < 0)
    {
        EVENT_TRACE_ERROR("[Poller::Init] create epollfd failed with err=" << error);
        return false;
    }
    return true;
}
bool Poller::UpdateChannel(ChannelPtr ch)
{
    ASSERT(ch != nullptr);
    int fd = ch->Fd();
    if (channelMap.count(fd) > 0)
    {
        return Update(EPOLL_CTL_MOD, ch);
    }
    if (ch->Available())
    {
        return Update(EPOLL_CTL_ADD, ch);
    }
    if (Update(EPOLL_CTL_DEL, ch))
    {
        channelMap.erase(fd);
    }
}

void Poller::Poll(ChannelList& activeChannels)
{
    static const int timeout = 10000;       // epoll timeout default as 10s, we can do some cmd audit work
    int numEvts = epoll_wait(
        epfd, 
        events.data(),
        static_cast<int>(events.size()),
        timeout);
    if (numEvts > 0)
    {
        FillActiveChannels(numEvts, activeChannels);
        return;
    }
    if (numEvts == 0)
    {
        EVENT_TRACE_DEBUG("[Poller::Poll] no event ocurrence");
        return;
    }
    EVENT_TRACE_ERROR("[Pollerc::Poll] epoll wait failed with err=" << error);
}
bool Poller::Update(int op, ChannelPtr channel)
{
    ASSERT(ch != nullptr);
    struct epoll_event event;
    memset(&event, 0, sizeof event);
    event.events = channel->InsterestedEvents();
    event.data.ptr = channel.get();
    int fd = channel->Fd();
    int err = epoll_ctl(epfd, op, fd, &event);
    if (err < 0)
    {
        EVENT_TRACE_ERROR("[Pollerc::Update] epoll ctrl failed with err=" << error);
        return false;
    }
    return true;
}
void Poller::FillActiveChannels(int num, ChannelList& list)
{
    for (int i = 0; i < num; i++)
    {
        Channel* ch = static_cast<Channel*>(events[i].data.ptr);
        ASSERT(ch != nullptr);
        ch->SetReceivedEvents(events[i].events); 
        list.push_back(ChannelPtr(ch));
    }
}